On freezing...
Our last release – 0.9.6 – already introduced the new bundle behaviour, set to replace what was formerly known as the freezer. With 0.9.7 we were able to streamline the code and workflow furher.
Updated information
This article has become partially obsolete, for now just see the documentation at wiki.merbivore.com
Bundle vs. Freeze
Our last release – 0.9.6 – already introduced the new bundle behaviour, set to replace what was formerly known as the freezer. The main goal of this concept is to bundle your dependencies with the application, making it independent of your system’s RubyGems. This makes your app portable, easy to deploy and it can be kept under source control more efficiently. With the upcoming 0.9.7 release, we were able to simplify the process, thanks to the introduction of merb.thor: Thor tasks for Merb.
The tasks come pre-installed with freshly generated apps, but are also available from merbivore.com/merb.thor for existing apps. Your existing app should run off a Merb >= 0.9.7 codebase, and you might need to regenerate your app’s Rakefile (run merb-gen app . and pick the Rakefile, be careful if you have custom tasks in there though).
You can use curl or wget to copy it to your local app or use the following to install it system-wide:
thor install http://merbivore.com/merb.thor
However, using a merb.thor file local to your app is regarded as a best practice. Note that you can bootstrap a complete Merb stack, just by having the Thor gem installed and the merb.thor tasks available somewhere on your system.
Introduction
One of the main concepts behind the tasks is that they are context-aware, meaning that most of them adapt themselves to how (and where) they are run: in the context of an application, they will operate locally, or outside any application they target the system in general.
The existence of a local subdirectory called gems will indicate that operations are to be local, possibly within a Merb app. For new applications the ./gems directory will be generated by default, but for existing apps you can either add it manually or even better, use the following task to get started:
thor merb:tasks:setup
This will not only create the ./gems directory, but will install Thor, Rake and Rspec into it, so that the application is setup correctly for executing any further tasks. To keep your merb.thor file up-to-date you can run merb:tasks:update.
When installing certain known components, like the gems stated above, you’ll notice that some special executable wrappers are generated into the application’s local ./bin directory.
These executables will pickup any dependencies from the local ./gems dir, effectively running from the bundled gems, instead of relying on the system’s rubygems – they will get precedence over them, but can fall back to the system if needed.
This is especially important for the merb command, which should always be run as the local variant bin/merb for any app that is setup to run with bundled gems. Due to the specific load order and rubygems executable wrappers, it’s impossible to load merb-core from local gems, just by running the regular merb command. So this is how it’s done instead:
bin/merb
The same is true for bin/merb-gen and bin/spec to name a few.
Step-by-step: how to bundle a stable Merb stack from RubyGems
Run the commands below to setup and run an application to use official gem release versions of merb-core, merb-more and a Rack adapter of your choice (optional): mongrel, emongrel, thin and ebb are currently supported. You can always install your own gems later using merb:gems:install.
From within your application:
$ thor merb:tasks:setup
$ bin/thor merb:stable -a mongrel
$ bin/merb
Step-by-step: how to bundle an edge Merb stack from Github
The following commands will setup a ./src directory and proceed to clone thor, extlib, rack, merb-core and merb-more from their official Github repositories. By specifying the—install option (needed for source installs) the gems are packaged and installed.
From within your application:
$ thor merb:tasks:setup
$ bin/thor merb:edge --install
$ bin/thor merb:gems:install mongrel
$ bin/merb
Step-by-step: how to keep up with Merb on your system
To keep up with Merb’s development, you can track from git HEAD. First you need to either have merb.thor installed globally:
$ thor install http://merbivore.com/merb.thor
Or locally, using curl to fetch it into your directory of choice, for example:
$ curl -L http://merbivore.com/merb.thor > merb.thor
Finally, once you have it setup, any time you want to update your system’s gems you can just run the next command from that directory:
$ sudo thor merb:edge --install
Just like a regular ‘gem install’, it is usually preceded by sudo to be able to install it directly on your system. Note that we shouldn’t have a local ./gems directory, and didn’t use thor merb:tasks:setup in our directory.
Using your own forks or git repo’s with commit access
With all of the merb:sources and merb:edge tasks you can specify a yaml file using the—sources option. Alternatively, if you have a file called ~/.merb/git-sources.yml in your home directory, it will be picked up from there. The structure of the file is just a regular yaml hash:
merb-core: git://github.com/my-repo/merb-core.git
merb-more: git://github.com/my-repo/merb-more.git
awesome: git//github.com/my-repo/awesome.git
The entries you define here are merged with the defaults, so you don’t have specify them all, plus you can add some non-standard gems and their sources. You can then refer to them by their yaml entry key: thor merb:source:clone awesome
Signing off
The three scenarios outlined above should be enough to get you started, though there are some other useful tasks remaining. I’ll introduce some of them briefly, and you’ll find more information in the inline comments in merb.thor itself. Don’t forget to run thor -T ...
# retrieve the source of a known gem from github
thor merb:source:clone dm-core
# same as above, but looking up git urls from sources.yml
# merb-core: git://github.com/fabien/merb-core.git
thor merb:source:update merb-core --sources ./path/to/sources.yml
# refresh all gem sources in ./src
thor merb:source:refresh
# install what has been retrieved by merb:source:clone or merb:source:update
thor merb:source:install dm-core
thor merb:source:merb:install merb-core
# for meta-gems it's sometimes easier to install specific parts
thor merb:source:install merb-more/merb-slices
# some gems feature compiled C extensions - these need to be recompiled on your
# target deployment platform (use Capistrano, God...)
thor merb:gems:redeploy
Comments
On September 15, 2008 at 05:42 mloughran says:
It’s great to see that further effort is being put into the freezing problem. I’m just wondering why none of these tasks make any use of git submodules. Is it because someone has decided that they are inherently not suitable, or is it just a feature that has not been added yet?
I’m personally not a fan of adding so much cruft to my repository. I would rather use a git SHA1 or a known gem version number if at all possible.
Also, does this approach in any way solve the old problem of gems with compiled components not being portable across OS’s?
On September 15, 2008 at 06:14 fabien says:
We’re planning to add git SHA1/Tag support to the current clone tasks. However, we decided that any gem install (regardless of it’s origin, being git or rubyforge) is to be installed through the rubygems management system. Doing so enables better integration, management and also recompiling gems on the target platform as part of the deployment process: thor merb:gems:redeploy
You won’t be adding ./src to your repository, so I’m not sure what you mean by ‘cruft’. In case of the ./gems directory: it gives us everything you need when deploying, including recompilation (since the gem source file is kept in a cache in ./gems/cache).
On September 21, 2008 at 11:52 piclez says:
Hi,
How can I install data_objects 0.9.6 into my merb app gems (freeze)? Since this version is not yet in rubyforge? Using: bin/thor merb:source:install data_objects will install 0.9.5.
Thanks,
Peter.
On September 21, 2008 at 12:19 piclez says:
Ok using the—cache option is possible to install my gems locally, thanks Thor!
On September 26, 2008 at 05:00 mloughran says:
Thanks fabien! I forgot to check back for a reply last week… The thor merb:gems:redeploy task is a great solution for the ‘gems must be recompiled on target platform’ problem.
By ‘cruft’ I meant that one has to commit all the code within /gems to the repository. There’s a heck of a lot more code in there than in the actual app. It’s partly a matter of personal preference, but for me I’d rather only keep the repository as lean as possible. Should I investigate the merb:dependencies:install task? Was that created with deployment in mind?
On September 27, 2008 at 16:58 lanaer says:
Just tried out creating, bundling & deploying a bundled merb app today… ran into a bunch of trouble with it. First, a broken english gem (that was just posted to rubyforge today), then I couldn’t even run bin/thor when the code was deployed to my server due to ruby path differences, and then redeploy wouldn’t work because the generated app was ignoring gems/cache (and I hadn’t noticed). All of these were fixable problems of course, but I figure that some others would be running into them at some point or another, so I documented my solutions.
On September 29, 2008 at 12:24 mlangenberg says:
Here’s an example config.ru, su you can use a bundled merb with passenger.
I haven’t tested this with minigems, maybe I broke something.begin require 'minigems' rescue LoadError require 'rubygems' end if File.directory?(gems_dir = File.join(Dir.pwd, 'gems')) || File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems')) $BUNDLE = true; Gem.clear_paths; Gem.path.unshift(gems_dir) end require 'merb-core' Merb::Config.setup(:merb_root => File.expand_path(File.dirname(__FILE__)), :environment => ENV['RACK_ENV']) Merb.environment = Merb::Config[:environment] Merb.root = Merb::Config[:merb_root] Merb::BootLoader.run run Merb::Rack::Application.newSign in to add your comment